/*
 * Diagrama.java
 *
 * Created on June 6, 2007, 10:29 PM
 *
 * To change this template, choose Tools | Template Manager
 * and open the template in the editor.
 */

package org.jerm.Interface.JGraph_Diagrama;

import java.awt.Dimension;
import java.awt.Point;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.MouseWheelEvent;
import java.awt.event.MouseWheelListener;
import java.awt.geom.Point2D;
import java.net.URL;

import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.ImageIcon;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JToolBar;
import javax.swing.ScrollPaneLayout;
import javax.swing.event.UndoableEditEvent;

import org.jerm.Interface.Principal;
import org.jgraph.JGraph;
import org.jgraph.event.GraphSelectionEvent;
import org.jgraph.event.GraphSelectionListener;
import org.jgraph.graph.DefaultPort;
import org.jgraph.graph.GraphUndoManager;

/**
 *
 * @author tiagofalcao
 */
public class Diagrama_JGraph extends JScrollPane implements GraphSelectionListener, KeyListener, MouseWheelListener
{
    //JGraph
    protected MyGraph graph;
    
    // Undo Manager
    protected GraphUndoManager undoManager;
    
    //Contador para posicao de insercao
    protected int insert;
    
    //Contador para determinar novo nome
    protected Integer count;
    
    //ToolBar
    protected JToolBar toolbar;
    protected AbstractAction undo;
    protected AbstractAction redo;
    
    protected AbstractAction remove;
    
    protected Action cut;
    protected Action copy;
    protected Action paste;
    
    protected AbstractAction tofront;
    protected AbstractAction toback;
    
    protected AbstractAction zoom;
    protected AbstractAction zoomin;
    protected AbstractAction zoomout;
    
    protected AbstractAction extra;
    protected AbstractAction export;
    
    // JPanel de Configuracao
    protected Diagrama_Configure Configuracao;
    public JPanel Configure_Panel;
    public Principal Principal;
    
    
    /** Creates a new instance of Diagrama */
    public Diagrama_JGraph(Principal Principal) {
        //////////////////////////////////////
        // JScrollPane
        setLayout(new ScrollPaneLayout.UIResource());
        setVerticalScrollBarPolicy(VERTICAL_SCROLLBAR_AS_NEEDED);
        setHorizontalScrollBarPolicy(HORIZONTAL_SCROLLBAR_AS_NEEDED);
	setViewport(createViewport());
	setVerticalScrollBar(createVerticalScrollBar());
	setHorizontalScrollBar(createHorizontalScrollBar()); 
        
        setViewportView(createGraph());//Cria Jgraph ja inserindo no JScrollPane
	setOpaque(true);
        updateUI();

	if (!this.getComponentOrientation().isLeftToRight()) 
	    viewport.setViewPosition(new Point(Integer.MAX_VALUE, 0));
        
        this.Principal=Principal;
        //////////////////////////////////
        // ToolBar
        createToolBar();
        
        /////////////////////////////////
        // Configure
        createConfiguracao();
        
        ///////////////////////
        //Undo Manager
        undoManager = new GraphUndoManager() {
                // Extend Superclass
                public void undoableEditHappened(UndoableEditEvent e) {
                    // First Invoke Superclass
                    super.undoableEditHappened(e);
                    // Update Undo/Redo Actions
                    updateHistoryButtons();
                }
        };
        
        ///////////////////////
        // Listeners
        installListeners();
        
        //////////////////////
        // Counter
        count=new Integer(0);
        insert=20;
        
    }
    
    /////////////////////////////////////////////////////////////////////////
    //
    // Graph
    //
    
    protected JGraph createGraph() {
            graph = new MyGraph(new MyModel());
            graph.getGraphLayoutCache().setFactory(new JGraphCellViewFactory());
            graph.setMarqueeHandler(new MyMarqueeHandler(this));
            return graph;
    }
    
    public MyGraph getGraph(){
        return graph;
    }
    
    /////////////////////////////////////////////////////////////////////////
    //
    // ToolBar
    //
    /**
     * Cria ToolBar para o Diagrama, pode ser recuperado com getToolBar()
     */
    protected void createToolBar(){
        toolbar = new JToolBar();
        toolbar.setFloatable(false);
        toolbar.setRollover(true);
        
        URL iconUrl;
        ImageIcon imageIcon;
        
        // Insere Entidadee
        iconUrl = getClass().getClassLoader().getResource("org/jerm/Interface/Icones/insert_entidade.gif");
        imageIcon = new ImageIcon(iconUrl);
        toolbar.add(new AbstractAction("", imageIcon) {
                public void actionPerformed(ActionEvent e) {
                        insertEntidade(new Point(insert, insert));
                        deslocaInsert();
                }
        });
        
        // Insere Relacionamento
        iconUrl = getClass().getClassLoader().getResource("org/jerm/Interface/Icones/insert_relacionamento.gif");
        imageIcon = new ImageIcon(iconUrl);
        toolbar.add(new AbstractAction("", imageIcon) {
                public void actionPerformed(ActionEvent e) {
                        insertRelacionamento(new Point(insert, insert));
                        deslocaInsert();
                }
        });
        
        // Insere Heranca
        iconUrl = getClass().getClassLoader().getResource("org/jerm/Interface/Icones/insert_heranca.gif");
        imageIcon = new ImageIcon(iconUrl);
        toolbar.add(new AbstractAction("", imageIcon) {
                public void actionPerformed(ActionEvent e) {
                        insertHeranca(new Point(insert, insert));
                        deslocaInsert();
                }
        });
        
	toolbar.addSeparator();
                
        // Remove
        iconUrl = getClass().getClassLoader().getResource("org/jerm/Interface/Icones/delete.png");
        imageIcon = new ImageIcon(iconUrl);
        remove=new AbstractAction("", imageIcon) {
                public void actionPerformed(ActionEvent e) {
                        if (!graph.isSelectionEmpty()) {
                                Object[] cells = graph.getSelectionCells();
                                cells = graph.getDescendants(cells);
                                int i=0;
                                while(i<cells.length){
                                    removeGraph(cells[i]);
                                    i++;
                                }
                                graph.getGraphLayoutCache().reload();
                        }
                }
        };
        toolbar.add(remove);
        remove.setEnabled(false);
        
	toolbar.addSeparator();
                
        // Recorta
        iconUrl = getClass().getClassLoader().getResource("org/jerm/Interface/Icones/cut.png");
        imageIcon = new ImageIcon(iconUrl);
        cut=new EventRedirector(javax.swing.TransferHandler.getCutAction(),imageIcon,this.graph);
        toolbar.add(cut);
        cut.setEnabled(false);
                
        // Copia
        iconUrl = getClass().getClassLoader().getResource("org/jerm/Interface/Icones/copy.png");
        imageIcon = new ImageIcon(iconUrl);
        copy=new EventRedirector(javax.swing.TransferHandler.getCopyAction(),imageIcon,this.graph);
        toolbar.add(copy);
        copy.setEnabled(false);
                
        // Cola
        iconUrl = getClass().getClassLoader().getResource("org/jerm/Interface/Icones/paste.png");
        imageIcon = new ImageIcon(iconUrl);
        paste=new EventRedirector(javax.swing.TransferHandler.getPasteAction(),imageIcon,this.graph);
        toolbar.add(paste);
        paste.setEnabled(true);
        
        
	toolbar.addSeparator();
                
        // Volta
        iconUrl = getClass().getClassLoader().getResource("org/jerm/Interface/Icones/undo.png");
        imageIcon = new ImageIcon(iconUrl);
        undo=new AbstractAction("", imageIcon) {
                public void actionPerformed(ActionEvent e) {
                        undo();
                }
        };
        toolbar.add(undo);
        undo.setEnabled(false);
        
        // Refaz
        iconUrl = getClass().getClassLoader().getResource("org/jerm/Interface/Icones/redo.png");
        imageIcon = new ImageIcon(iconUrl);
        redo=new AbstractAction("", imageIcon) {
                public void actionPerformed(ActionEvent e) {
                        redo();
                }
        };
        toolbar.add(redo);
        redo.setEnabled(false);
        
        toolbar.addSeparator();
        
        // To Front
        iconUrl = getClass().getClassLoader().getResource("org/jerm/Interface/Icones/tofront.gif");
        imageIcon = new ImageIcon(iconUrl);
        tofront=new AbstractAction("", imageIcon) {
                public void actionPerformed(ActionEvent e) {
                        if (!graph.isSelectionEmpty())
                                toFront(graph.getSelectionCells());
                }
        };
        toolbar.add(tofront);
        tofront.setEnabled(false);
        
        // To Back
        iconUrl = getClass().getClassLoader().getResource("org/jerm/Interface/Icones/toback.gif");
        imageIcon = new ImageIcon(iconUrl);
        toback=new AbstractAction("", imageIcon) {
                public void actionPerformed(ActionEvent e) {
                        if (!graph.isSelectionEmpty())
                                toBack(graph.getSelectionCells());
                }
        };
        toolbar.add(toback);
        toback.setEnabled(false);
        
        
        toolbar.addSeparator();
        // Zoom 100%
        iconUrl = getClass().getClassLoader().getResource("org/jerm/Interface/Icones/zoom-100.png");
        imageIcon = new ImageIcon(iconUrl);
        zoom=new AbstractAction("", imageIcon) {
                    public void actionPerformed(ActionEvent e) {
                            graph.setScale(1.0);
                    }
        };
        toolbar.add(zoom);
        toback.setEnabled(true);
        
        // Zoom +
        iconUrl = getClass().getClassLoader().getResource("org/jerm/Interface/Icones/zoom-in.png");
        imageIcon = new ImageIcon(iconUrl);
        zoomin=new AbstractAction("", imageIcon) {
                    public void actionPerformed(ActionEvent e) {
                            graph.setScale(2 * graph.getScale());
                    }
        };
        toolbar.add(zoomin);
        toback.setEnabled(true);
        
        // Zoom -
        iconUrl = getClass().getClassLoader().getResource("org/jerm/Interface/Icones/zoom-out.png");
        imageIcon = new ImageIcon(iconUrl);
        zoomout=new AbstractAction("", imageIcon) {
                    public void actionPerformed(ActionEvent e) {
                            graph.setScale(graph.getScale()/2);
                    }
        };
        toolbar.add(zoomout);
        toback.setEnabled(true);
        
        toolbar.addSeparator();
        
        // Extra
        iconUrl = getClass().getClassLoader().getResource("org/jerm/Interface/Icones/screenshot.png");
        imageIcon = new ImageIcon(iconUrl);
        export=new AbstractAction("", imageIcon) {
                    public void actionPerformed(ActionEvent e) {
                            graph.exportImage();
                    }
        };
        toolbar.add(export);
        export.setEnabled(true);
        
        // Extra
        iconUrl = getClass().getClassLoader().getResource("org/jerm/Interface/Icones/ok.png");
        imageIcon = new ImageIcon(iconUrl);
        extra=new AbstractAction("", imageIcon) {
                    public void actionPerformed(ActionEvent e) {
                            Principal.Tabelas.mapear();
                            Principal.Abas.setSelectedIndex(1);
                    }
        };
        toolbar.add(extra);
        extra.setEnabled(true);
        
        
        
        
    }
    /**
     * Retorna JToolBar do Diagrama
     * @return JToolBar ToolBar de Manipulacao do Diagrama.
     */
    public JToolBar getToolBar(){
        return toolbar;
    }
    
    public void removeGraph(Object o){
        Object[] edges;
        if(o.getClass()==Aresta.class){
            Aresta edge=(Aresta)o;
            if(edge.isTotal()){
                ((Entidade)((DefaultPort)edge.getTarget()).getUserObject()).setFraca(false);
                ((Relacionamento)((DefaultPort)edge.getSource()).getUserObject()).setTotal(false);
            }else if(edge.isHeranca() && ((DefaultPort)edge.getTarget()).getUserObject().getClass()==Heranca.class){
                graph.getModel().remove(graph.getArestas(((DefaultPort)edge.getTarget()).getUserObject(),Aresta.SOURCE));
            }else if(!edge.isHeranca()){
                Relacionamento r=(Relacionamento)((DefaultPort)edge.getSource()).getUserObject();
                if(r.isTotal()){
                    Object[] redges=graph.getArestas(r,Aresta.SOURCE);
                    if(redges.length==2)
                        if(((Aresta)redges[0]).isTotal()){
                            ((Entidade)((DefaultPort)((Aresta)redges[0]).getTarget()).getUserObject()).setFraca(false);
                            ((Relacionamento)((DefaultPort)((Aresta)redges[0]).getSource()).getUserObject()).setTotal(false);
                            ((Aresta)redges[0]).setTotal(false);
                        }else{
                            ((Entidade)((DefaultPort)((Aresta)redges[1]).getTarget()).getUserObject()).setFraca(false);
                            ((Relacionamento)((DefaultPort)((Aresta)redges[1]).getSource()).getUserObject()).setTotal(false);
                            ((Aresta)redges[1]).setTotal(false);
                        }
                }
            }
        }else{
            int i=0;
            edges=graph.getArestas(o,Aresta.ANY);
            while(i<edges.length){
                this.removeGraph(edges[i]);
                i++;
            }
        }
        edges=new Object[1];
        edges[0]=o;
        graph.getModel().remove(edges);
    }
    
    /////////////////////////////////////////////////////////////////////////
    //
    // Blocos
    //
    
    /**
     * Insere uma Entidade no Local indicado
     * @param Local Point2D com coordenadas x e y do local de insercao
     */
    protected void insertEntidade(Point2D Local){
        Entidade ent=new Entidade(Local.getX(),Local.getY(),"Entidade "+ count);
        count++;
        graph.getGraphLayoutCache().insert(ent);
    }
    
    /**
     * Insere um Relacionamento no Local indicado
     * @param Local Point2D com coordenadas x e y do local de insercao
     */
    protected void insertRelacionamento(Point Local){
        Relacionamento rel=new Relacionamento(Local.x,Local.y,"Relacao "+ count);
        count++;
        graph.getGraphLayoutCache().insert(rel);
    }
    
    /**
     * Insere uma Heranca no Local indicado
     * @param Local Point2D com coordenadas x e y do local de insercao
     */
    protected void insertHeranca(Point Local){
        Heranca her=new Heranca(Local.x,Local.y,"");
        count++;
        graph.getGraphLayoutCache().insert(her);
    }
    
    /**
     * Movimenta a Posicao de Insercao Padrao
     */
    protected void deslocaInsert(){
        if(insert==120){
            insert=20;
        }else{
            insert+=20;
        }
    }
    
    
    
    /////////////////////////////////////////////////////////////////////////
    //
    // Edge                 [XXX]
    //
    
    /**
     * Insere uma Linha entre as Portas de Source e Target
     * Avalia se a linha eh valida para a teoria, e pode inverter a posicao de source com target
     * Alem de Determinar o Tipo de Linha a Ser usada
     * @param source_port DefaultPort de Inicio da Linha
     * @param target_port DefaultPort de Target da Linha
     */
    public void connect(DefaultPort source_port, DefaultPort target_port) {
            // Construct Edge with no label
            Aresta edge = new Aresta();
            if (graph.getModel().acceptsSource(edge, source_port)
                            && graph.getModel().acceptsTarget(edge, target_port)) {
                    
                    // Create a Map thath holds the attributes for the edge
                    Object item_source=source_port.getUserObject();
                    Object item_target=target_port.getUserObject();
                
                
                    if(item_source.getClass()==item_target.getClass() && item_target.getClass()!=Relacionamento.class)
                        return;
                    
                    else if((item_source.getClass()==Relacionamento.class && item_target.getClass()==Heranca.class)||
                            (item_target.getClass()==Relacionamento.class && item_source.getClass()==Heranca.class))
                        return;
                    
                    else if(item_source.getClass()==Entidade.class && item_target.getClass()==Relacionamento.class){
                        //if(countAresta(target_port,source_port)>1)
                        //    return;
                        edge.setCardinalidade(0,2);
                        edge.setSource(target_port);
                        edge.setTarget(source_port);
                    
                    }else if(item_target.getClass()==Entidade.class && item_source.getClass()==Relacionamento.class){
                        //if(countAresta(source_port,target_port)>1)
                        //    return;
                        edge.setCardinalidade(0,2);
                        edge.setSource(source_port);
                        edge.setTarget(target_port);
                    
                    }else if(graph.hasAresta(source_port,target_port))
                        return;
                    
                    else if(item_source.getClass()==Entidade.class && item_target.getClass()==Heranca.class){
                        if(graph.hasPaiHeranca(target_port))  //Bloqueia Dois pais de uma heranca
                            return;
                        
//                        //Bloqueia Arvore de Heranca (2)
//                        Object[] edges=graph.getArestas(item_source,Aresta.TARGET);
//                        int i=0;
//                        while(i<edges.length){
//                            if(((Aresta)edges[i]).isHeranca()){
//                                return;
//                            }
//                            i++;
//                        }// Acaba Bloqueio
                        
                        edge.setHeranca();
                        edge.setSource(source_port);
                        edge.setTarget(target_port);
                        
                    }else if(item_target.getClass()==Entidade.class && item_source.getClass()==Heranca.class){
                        //Bloquei Filho sem Pai,e Filho de 2 Pais
                        if(graph.hasFilhoHeranca(target_port) || !graph.hasPaiHeranca(source_port)) 
                            return;
//                        if(graph.getArestas(item_target,Aresta.SOURCE).length>0) //Bloqueia Arvore de Heranca (1)
//                            return;
                        edge.setHeranca();
                        edge.setSource(source_port);
                        edge.setTarget(target_port);
                    
                    }else{
                        edge.setSource(source_port);
                        edge.setTarget(target_port);
                        
                    }
                    // Insert the Edge and its Attributes
                    graph.getGraphLayoutCache().insertEdge(edge, source_port, target_port);
            }
    }
    
    

    
    /////////////////////////////////////////////////////////////////////////
    //
    // Graph Listener
    //	
    // Hook for subclassers
    protected void installListeners() {
            // Add Listeners to Graph
            //
            // Register UndoManager with the Model
            graph.getModel().addUndoableEditListener(undoManager);
            // Update ToolBar based on Selection Changes
            graph.getSelectionModel().addGraphSelectionListener(this);
            // Listen for Delete Keystroke when the Graph has Focus
            graph.addKeyListener(this);
            graph.addMouseWheelListener(this);
            //graph.getModel().addGraphModelListener(statusBar);
    }

    // Hook for subclassers
    protected void uninstallListeners() {
            graph.getModel().removeUndoableEditListener(undoManager);
            graph.getSelectionModel().removeGraphSelectionListener(this);
            graph.removeKeyListener(this);
            graph.removeMouseWheelListener(this);
            //graph.getModel().removeGraphModelListener(statusBar);
    }
        
    // From GraphSelectionListener Interface
    public void valueChanged(GraphSelectionEvent e) {
            // Update Button States based on Current Selection
            boolean enabled = !graph.isSelectionEmpty();
            remove.setEnabled(enabled);
            tofront.setEnabled(enabled);
            toback.setEnabled(enabled);
            copy.setEnabled(enabled);
            cut.setEnabled(enabled);
            updateConfiguracao(enabled);
    }
    
    /////////////////////////////////////////////////////////////////////////
    //
    // KeyListener for Delete KeyStroke
    //
    public void keyReleased(KeyEvent e) {
    }

    public void keyTyped(KeyEvent e) {
    }

    public void keyPressed(KeyEvent e) {
            // Listen for Delete Key Press
            if (e.getKeyCode() == KeyEvent.VK_DELETE)
                // Execute Remove Action on Delete Key Press
                remove.actionPerformed(null);
            else if(e.getModifiersEx()== KeyEvent.CTRL_DOWN_MASK && e.getKeyCode() == KeyEvent.VK_Z)
                undo.actionPerformed(null);
            else if(e.getModifiersEx()== KeyEvent.CTRL_DOWN_MASK && e.getKeyCode() == KeyEvent.VK_R)
                redo.actionPerformed(null);
           // else if(e.getModifiersEx()== KeyEvent.CTRL_DOWN_MASK && e.getKeyCode() == KeyEvent.VK_X)
            //    cut.actionPerformed(null);
            //else if(e.getModifiersEx()== KeyEvent.CTRL_DOWN_MASK && e.getKeyCode() == KeyEvent.VK_C)
           //     copy.actionPerformed(null);
            //else if(e.getModifiersEx()== KeyEvent.CTRL_DOWN_MASK && e.getKeyCode() == KeyEvent.VK_V)
           //     paste.actionPerformed(null);
            
    }
    
    //////////////////////////////////////////////////////////////////////////
    //
    // To Front / To Back
    //
    
      // Brings the Specified Cells to Front
    public void toFront(Object[] c) {
            graph.getGraphLayoutCache().toFront(c);
    }
    // Sends the Specified Cells to Back
    public void toBack(Object[] c) {
            graph.getGraphLayoutCache().toBack(c);
    }


    //////////////////////////////////////////////////////////////////////////////
    //
    // Undo e Redo
    //
    
    // Undo the last Change to the Model or the View
    public void undo() {
        try {
            undoManager.undo(graph.getGraphLayoutCache());
        } catch (Exception ex) {
            System.err.println(ex);
        } finally {
            updateHistoryButtons();
        }
    }
    
    // Redo the last Change to the Model or the View
    public void redo() {
        try {
            undoManager.redo(graph.getGraphLayoutCache());
        } catch (Exception ex) {
            System.err.println(ex);
        } finally {
            updateHistoryButtons();
        }
    }

    protected void updateHistoryButtons(){
        // The View Argument Defines the Context
        undo.setEnabled(undoManager.canUndo(graph.getGraphLayoutCache()));
        redo.setEnabled(undoManager.canRedo(graph.getGraphLayoutCache()));
    }
    
    /////////////////////////////////////////////////////////////////////////////
    //
    // Configracao
    //
    
    public void createConfiguracao(){
        Configure_Panel=new JPanel();
        Configure_Panel.setMaximumSize(new Dimension(200,400));
        Configure_Panel.setMinimumSize(new Dimension(200,400));
        Configure_Panel.setPreferredSize (new Dimension(200,400));
    }
    
    public void updateConfiguracao(boolean enabled){
            this.Configure_Panel.removeAll();
        if(enabled){
            //Object[] cells = graph.getSelectionCells();
            //this.Configure_Panel.add(new Diagrama_Configure(graph,cells[0],this));
            this.Configure_Panel.add(new Diagrama_Configure(graph));
        }
            this.Configure_Panel.updateUI(); //
    }

    public void mouseWheelMoved(MouseWheelEvent e) {
        int notches = e.getWheelRotation();
        if (notches < 0) {
            zoomin.actionPerformed(null);
        } else {
            zoomout.actionPerformed(null);
        }
    }
    
    

}
